-module(rtps_udp).

%%% Some functions to calculate locators

%% -include_lib("kernel/include/logger.hrl").
-include("rtps.hrl").
-include("rtps_udp.hrl").

-export([multicast_locator/3, unicast_locator/4]).

%%%===================================================================
%%% API
%%%===================================================================

-spec multicast_locator(Domain_id, Entity_id, Opts) -> Result when
      Domain_id :: rtps:domain_id(),
      Entity_id :: rtps:entity_id(),
      Opts :: [proplists:property()],
      Result :: {ok, Locator} | {error, Reason},
      Locator :: rtps:locator(),
      Reason :: term().
%% @doc Return a multicast locator
multicast_locator(Domain_id, Entity_id, Opts) ->
    PB = proplists:get_value(port_base_number, Opts, ?PB),
    DG = proplists:get_value(domain_id_gain, Opts, ?DG),
    D0 = proplists:get_value(d0, Opts, ?d0),
    D2 = proplists:get_value(d2, Opts, ?d2),
    case lists:member(Entity_id, discovery_entity_ids()) of
	true ->
	    multicast_locator(Domain_id, PB, DG, D0);
	false ->
	    multicast_locator(Domain_id, PB, DG, D2)
    end.

-spec unicast_locator(Domain_id, Participant_id, Entity_id, Opts) -> Result when
      Domain_id :: rtps:domain_id(),
      Participant_id :: rtps:participant_id(),
      Entity_id :: rtps:entity_id(),
      Opts :: [proplists:property()],
      Result :: {ok, Locator} | {error, Reason},
      Locator :: rtps:locator(),
      Reason :: term().
%% @doc Return a multicast locator
unicast_locator(Domain_id, Participant_id, Entity_id, Opts) ->
    PB = proplists:get_value(port_base_number, Opts, ?PB),
    DG = proplists:get_value(domain_id_gain, Opts, ?DG),
    PG = proplists:get_value(participant_id_gain, Opts, ?PG),
    D1 = proplists:get_value(d1, Opts, ?d1),
    D3 = proplists:get_value(d3, Opts, ?d3),
    case lists:member(Entity_id, discovery_entity_ids()) of
	true ->
	    unicast_locator(Domain_id, Participant_id, PB, DG, PG, D1);
	false ->
	    unicast_locator(Domain_id, Participant_id, PB, DG, PG, D3)
    end.

discovery_entity_ids() ->
    ['SPDPbuiltinParticipantWriter', 'SPDPbuiltinParticipantReader',
     'SEDPbuiltinTopicWriter', 'SEDPbuiltinTopicReader',
     'SEDPbuiltinPublicationsWriter', 'SEDPbuiltinPublicationsReader',
     'SEDPbuiltinSubscriptionsWriter', 'SEDPbuiltinSubscriptionsReader',
     'BuiltinParticipantMessageWriter', 'BuiltinParticipantMessageReader'].

%%%===================================================================
%%% Internal 
%%%===================================================================

%% TODO: From OpenDDS Developers guide by ociweb.com: The
%% default_to_ipv6 and port_offset options effect how default
%% multicast group addresses are selected. If default_to_ipv6 is set
%% to “1” (enabled), then the default IPv6 address will be used
%% ([FF01::80]). The port_offset option determines the default port
%% used when the group address is not set and defaults to 49152.

multicast_locator(Domain_id, PB, DG, D) ->    
    {ok, IPv4_address} = inet:parse_ipv4_address(?DEFAULT_UDPv4_MULTICAST_ADDRESS),
    case PB + DG * Domain_id + D of
	Port when Port > 0, Port =< 16#ffff ->
	    #locator{kind = udpv4, address = IPv4_address, port = Port};
	    %% #locator_udpv4{address = IPv4_address, port = Port};
	_ ->
	    {error, ipv4_port_range_error}
    end.

unicast_locator(Domain_id, Participant_id, PB, DG, PG, D) ->    
    {ok, IPv4_address} = inet:parse_ipv4_address("0.0.0.0"),
    case PB + DG * Domain_id + D + PG * Participant_id of	
	Port when Port > 0, Port =< 16#ffff ->
	    #locator{kind = udpv4, address = IPv4_address, port = Port};
	    %% #locator_udpv4{address = IPv4_address, port = Port};
	_ ->
	    {error, ipv4_port_range_error}
    end.
